package com.zjuee.gateway.config;

import com.zjuee.gateway.DynamicLoadUrl;
import com.zjuee.gateway.MyRememberMe;
import com.zjuee.gateway.MySessionDAO;
import com.zjuee.gateway.MySessionManager;
import com.zjuee.gateway.filter.MyFormAuthenticationFilter;
import com.zjuee.gateway.filter.MyPermissionsAuthorizationFilter;
import com.zjuee.gateway.filter.MyRolesAuthorizationFilter;
import com.zjuee.gateway.realm.MyShiroRealm;
import com.zjuee.gateway.utils.SecurityUtil;
import com.zjuee.service.auth.service.UserService;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

import javax.annotation.Resource;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author wu liang
 * @since 2020/8/13 9:54
 */
@Configuration
public class ShiroConfig {

    @Resource
    private DynamicLoadUrl loadUrl;
    @Resource
    private UserService userService;
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * Shiro基本配置
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //定义过滤器
        Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
        filters.put("authc",formAuthenticationFilter());
        filters.put("hasRoles", new MyRolesAuthorizationFilter());
        filters.put("hasPerms", new MyPermissionsAuthorizationFilter());
        shiroFilterFactoryBean.setFilters(filters);

        // 定义拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

        //todo 开发阶段，放行所有url
        //  filterChainDefinitionMap.put("/**", "anon");

        //注意过滤器配置顺序 顺序判断 不能颠倒
        // logout 忽略验证，在Controller中自定义处理退出逻辑
//        filterChainDefinitionMap.put("/api/logout", "anon");
//
//        filterChainDefinitionMap.put("/api/core/file/*/*/downLoad", "anon");
//        filterChainDefinitionMap.put("/api/core/sharding/**", "anon");


        // 加入自定义的权限获取
        filterChainDefinitionMap.putAll(loadUrl.loadFilterChainDefinitions());

//        filterChainDefinitionMap.put("/api/area/areas/*", "anon");
//        filterChainDefinitionMap.put("/api/core/dict/findDict/*", "anon");
        filterChainDefinitionMap.put("/api/**", "authc");

        // 定义登录URL，请求此url会在filter中自动调用登录逻辑
        shiroFilterFactoryBean.setLoginUrl("/api/login/login");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 获取自定义的Filter，此处不能声明为Bean，防止重复注入为Filter
     */
    public FormAuthenticationFilter formAuthenticationFilter(){
        MyFormAuthenticationFilter filter = new MyFormAuthenticationFilter();
        filter.setUserService(userService);
        return filter;
    }

    /**
     * 安全管理器 核心
     */
    @Bean
    public SecurityManager securityManager(DefaultWebSessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        // 自定义session管理
        securityManager.setSessionManager(sessionManager);
        // 使用内存缓存
        securityManager.setCacheManager(new MemoryConstrainedCacheManager());
        // 自定义rememberMe处理器
        securityManager.setRememberMeManager(new MyRememberMe());
        return securityManager;
    }

    /**
     * 凭证匹配器  加密算法
     * （由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了）
     */
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次数，比如散列两次，相当于 md5(md5(""));
        return hashedCredentialsMatcher;
    }

    /**
     * 定义Realm
     */
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        myShiroRealm.setUserService(userService);
        return myShiroRealm;
    }


    /**
     * session 管理对象
     */
    @Bean
    public DefaultWebSessionManager sessionManager(SessionDAO sessionDAO) {
        DefaultWebSessionManager sessionManager = new MySessionManager();
        sessionManager.setSessionDAO(sessionDAO);
        // 禁止将sessionId写入浏览器的cookie中
        sessionManager.setSessionIdCookieEnabled(false);
        // 禁止启动session校验程序
        sessionManager.setSessionValidationSchedulerEnabled(false);
        sessionManager.setGlobalSessionTimeout(SecurityUtil.TOKEN_EXPIRES_HOUR * 3600000);
        return sessionManager;
    }

    /**
     * 定义redis的session管理器
     */
    @Bean
    public SessionDAO sessionDAO() {
        MySessionDAO sessionDAO = new MySessionDAO();
        sessionDAO.setRedisTpl(redisTemplate);
        return sessionDAO;
    }

    /**
     * 开启shiro aop注解支持.
     * 使用代理方式;所以需要开启代码支持;
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
